<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body></body>
  <script>
    // 原始数据
    const data = { text: "hello world" };
    // 存储副作用函数 重新设计的桶
    const bucket = new WeakMap();
    // 存储副作用函数的变量
    let activeEffect;
    // 注册副作用函数的函数
    const effect = (fn) => {
      activeEffect = fn;
      fn();
    };
    // track 获取
    const track = (target, key) => {
      // 没有副作用函数直接返回
      if (!activeEffect) return target[key];
      // 获取桶以targe作为键名，以set作为值
      let desMap = bucket.get(target);
      // 如果这个桶中之前没有收集这个对象的依赖那么就新建一个  Map
      if (!desMap) {
        bucket.set(target, (desMap = new Map()));
      }
      // 获取桶中对应对象的 desMap 中的 以键名作为key,以副作用函数集合作为值的数据
      let desSet = desMap.get(key);
      // 如果该key为空，那么重新设置set集合
      if (!desSet) {
        desMap.set(key, (desSet = new Set()));
        console.log("desSet: ", desSet);
      }
      // 收集依赖
      desSet.add(activeEffect);
    };

    // trigger 设置
    const trigger = (target, key) => {
      // 根据响应对象获取 依赖集合
      const desMap = bucket.get(target);
      if (!desMap) return true;
      // 根据key获取对应属性的依赖集合
      const desSet = desMap.get(key);
      if (!desSet) return true;
      // 执行副作用函数桶
      desSet.forEach((fn) => {
        fn();
      });
    };

    const obj = new Proxy(data, {
      get(target, key) {
        track(target, key);
        return target[key];
      },
      set(target, key, newVal) {
        target[key] = newVal;
        trigger(target, key);
      },
    });
    // 注册副作用函数
    effect(() => {
      console.log("注册副作用函数");
      document.body.innerHTML = obj.text;
    });

    setTimeout(() => {
      obj.text = "test";
    }, 1000);

    // 不存在的属性也触发了响应式
    setTimeout(() => {
      obj.noExit = "test";
    }, 3000);
  </script>
</html>
